package cluster

import (
	"github.com/samuel/go-zookeeper/zk"
	log "github.com/sirupsen/logrus"
	"sort"
	"xc/app/zookeeper"
)

// Nodes 集群节点
type Nodes struct {
	path     string   //zk路径
	data     []byte   //zk数据
	flags    int32    //zk标示
	acl      []zk.ACL //zk权限
	children []string //当前拥有的子节点列表
	childNum int      //当前拥有的子节点数量
}

var nodes *Nodes

//InitNodes 集群节点信息初始化
func InitNodes() {
	ns := new(Nodes)
	ns.initData()
	ns.check()

	nodes = ns
}

func (ns *Nodes) initData() {
	ns.path = zookeeper.GetNodesPath()
	ns.data = []byte("nodes info")
	ns.acl = zk.WorldACL(zk.PermAll)
	ns.flags = 0
}

// check 检查集群节点
func (ns *Nodes) check() {
	exist, _, err := zookeeper.Connect().Exists(ns.path)
	if err != nil {
		log.Panic("[nodes]", err.Error())
	}

	if !exist {
		log.Info("[nodes]集群节点不存在")

		ret, err := zookeeper.Connect().Create(ns.path, ns.data, ns.flags, ns.acl)
		if err != nil {
			log.Panic("[nodes]集群节点创建失败")
		}
		log.Info("[nodes]集群节点创建成功", ret)
	} else {
		log.Info("[nodes]集群节点已存在")
	}
}

func NodesWatch() {
	nodes.watch()
}

// Watch 监听节点变化
//
// 目前仅当 EventNodeChildrenChanged 下面的 子节点 增加或者删除的情况才做出响应
func (ns *Nodes) watch() {
	children, _, watchEvent, err := zookeeper.Connect().ChildrenW(ns.path)
	if err != nil {
		log.Panic("[nodes]", err)
	}
	log.Info("[nodes]集群当前所有子节点")
	for key, child := range children {
		log.Info(key, child)
	}

	for {
		select {
		case nodeEvent := <-watchEvent:
			{
				log.Info("[nodes]path:", nodeEvent.Path)
				log.Info("[nodes]type:", nodeEvent.Type.String())

				switch nodeEvent.Type {
				case zk.EventNodeCreated:
					log.Info("[nodes]监听到节点创建", nodeEvent.Path)
				case zk.EventNodeDeleted:
					log.Info("[nodes]监听到节点删除", nodeEvent.Path)
				case zk.EventNodeDataChanged:
					log.Info("[nodes]监听到节点变化", nodeEvent.Path)
				case zk.EventNodeChildrenChanged:
					log.Info("[nodes]监听到节点下面的子节点变化", nodeEvent.Path)

					// leader选举
					ns.LeaderElection()

					// 节点工作
					ns.DoWork()

					// 循环监听
					ns.watch()
				default:
					log.Info("[nodes]监听到其他事件", nodeEvent)
				}
			}
		}
	}
}

// LeaderElection leader选举
//
// 选举算法为经过排序后最小的key为leader，其余均为follower
// 利用了zookeeper的原子递增特性
//
// 如果leader宕机或者离线，则重新发起选举
// 每次监听到节点发生变化，自身节点应用都会进行一次leader选举判断，判断自己是否应该为leader
func (ns *Nodes) LeaderElection() {
	children, _, err := zookeeper.Connect().Children(ns.path)
	if err != nil {
		panic(err)
	}

	sort.Strings(children)

	log.Info("[nodes]集群当前所有子节点")
	for key, child := range children {
		log.Info("[nodes]", key, ":", child)
	}

	for key, child := range children {
		if key == 0 && child == node.name {
			// leader
			node.leader = true
			log.Info("[nodes]我是leader节点")
		}
	}

	// follower
	if node.leader == false {
		log.Info("[nodes]我是follower节点")
	}

	ns.children = children
	ns.childNum = len(children)
}

//DoWork 开始工作
func (ns *Nodes) DoWork() {
	worker.DoWork()
}
